<?php


    /**
     * Clase que sirve para armar tablas completas con su paginador, filtros
     * y ordenaciones. y 

     */

     
class sfTable extends sfTableWidget{
    
    public  $tdSchema;
    public  $thSchema;
    
    
    private $filter;
    private $pager = null;
    private $orderBy = null;
    
    private $query = null;
    private $baseQuery = null;    
    
    
    private $data = array();
    

   
    
    public $aliasArray = array();
    


    
    /**
     * Constructor.
     *
     * Available options:
     *
     *  * id_format:       The format for the generated HTML id attributes (%s by default)
     *  * is_hidden:       true if the form widget must be hidden, false otherwise (false by default)
     *  * default:         The default value to use when rendering the widget
     *  * title:           The title to use when the widget is rendered
     *  * link:            The link of the widget
     *  * name:            The name of the widget in the schema (th or td)
     *  * template         The template for render de widget. Must include %content% 
     *  * model:           The base model for the table
     *  * name:            The name of the table and his subparts
     * 
     * @param array $options     An array of options
     * @param array $attributes  An array of default HTML attributes
     *
     * @see sfWidget
     */
    public function __construct($options = array(), $attributes = array())
    {
        
        $this->addOption('model', '');
        $this->addOption('name', 'table');        
        $this->addOption('footerContent', '');
        $this->addOption('pageSize', '5');        
        $this->addOption('page', '0');                
        $this->addOption('action', 'table');        
        
        $this->addOption('fixedParameters', array());         
        
        $this->addOption('filterTemplate', '%General%');                
        
        parent::__construct($options, $attributes);

        if($this->getOption('template') == '%content%')$this->setOption('template', '%filter%%pager%%orderBy%%table%');        
        
        
        $this->tdSchema = new sfTdSchema();
        $this->thSchema = new sfThSchema();
        
        $this->filter = new sfTableFormFilter(array(),array(),false);        
        $this->filter->setParent($this);
        $this->filter->model = $this->getOption('model');
  
        $this->filter->getWidgetSchema()->setNameFormat($this->getName().'Filter[%s]');         
        
        $this->query= Doctrine_Query::create()
             ->from($this->getOption('model').' a0');
        
        $this->query->addSelect('a0'.'.id');
        
        $this->pager = new sfTablePager();
        $this->pager->setParent($this);
        
        $this->orderBy = new sfTableOrderBy();
        $this->orderBy->setParent($this);
      

    }
    
    
    public function getName() {
        
        return $this->getOption('name');
        
    }
    
    public function getFilterParameters() {
        
        return $this->getName().'Filter';
        
    }

    public function getTableParameters() {
        
        return $this->getName().'Values';
        
    }
    
    
    public function setFooterContent($footerContent) {
        
        $this->setOption('footerContent', $footerContent);
        return $this;
        
    }
    
    
    public function setPage($page) {
        
        $this->setOption('page',$page);
        return $this;        
        
    }

    
    
    public function getPageSize() {
        
        return $this->getOption('pageSize');
        
    }
    
    public function setPaginator($pager) {
        
        $this->pager = $pager;
        
    }
    

    public function getQuery() {
        
        return $this->query;
        
    }
    

    public function getBaseQuery() {
        
        return $this->baseQuery;
        
    }
    
    public function setQuery($query) {
        
        $this->query = $query;
        return $this;        
        
    }
    
    //Se configura la accion en la que se muestra la tabla 
    //Es la dir a la que va el form cuando se filtra o pagina
    
    public function setAction($action) {
        
        $this->setOption('action', $action);
        return $this;
    }

    
    public function getAction() {
        
        return $this->getOption('action');
    }
    
    public function setPageSize($pageSize) {
        
        $this->setOption('pageSize',$pageSize);
        return $this;
        
    }
    
    public function addFixedParameter($fixedParameterName, $value) {
        
          $fixedParameters = $this->getOption('fixedParameters');                
          $fixedParameters[$fixedParameterName] = $value;
          $this->setOption('fixedParameters', $fixedParameters);     
          return $this;
    }
    
    public function getFixedParameters() {
      return $this->getOption('fixedParameters');        
    }
    

    public function configureFilter() {
    

        $lastPos = 50;          //la idea de esto es mandar para el fondo los widget que no vienen con posicion fijada
        
        foreach($this->tdSchema as $k => $td)        
        {
            if($td->isModelTd())
            {
                $alias = $this->getAlias($td->getField());
                $this->filter->filterFields[$k] = $alias.'.'.$td->getOption('leaf');
            }
        }
        
        
        foreach($this->thSchema as $k => $th)
        {
            
            if($th->isFilterable())
            {
                
                if($th->getFilterPosition() == '')$th->setFilterPosition($lastPos++);
                
                
                $positionsArray[$th->getFilterPosition()] = $k;
                $type = $th->getFilterType();
                $this->filter->filterTypes[$k] = $type;
                
                
                if($th->getWidget() == null) 
                {
                        switch ($type)
                        {
                            case 'text':

                                $widget = new sfWidgetFormFilterInput(array('with_empty' => false));
                                if(($label = $th->getFilterLabel()) != '')
                                {
                                    $widget->setLabel($label);      
                                }else
                                {
                                    $widget->setLabel($this->thSchema[$k]->getText());                                          
                                }
                                $this->filter->setWidget($k, $widget);
                                $validator = new sfValidatorPass(array('required' => false));
                                $this->filter->setValidator($k, $validator);
                                
                                if($th->getOption('multiText'))
                                        $this->filter->filterTypes[$k] = 'multiText'; //tengo que cambiar el tipo de filtro 

                            break;

                            case 'number':

                                $widget = new sfWidgetFormFilterInput(array('with_empty' => false));
                                if(($label = $th->getFilterLabel()) != '')
                                {
                                    $widget->setLabel($label);      
                                }else
                                {
                                    $widget->setLabel($this->thSchema[$k]->getText());                                          
                                }
                                $this->filter->setWidget($k, $widget);
                                $validator = new sfValidatorPass(array('required' => false));
                                $this->filter->setValidator($k, $validator);

                            break;
                            
                            case 'dateRange':
                                    //------- FECHAS -------
                                    //  *** Fecha Solicitud Orden
                                    //Armo los "from" y "to" auxiliares para cada rango de fechas

                                $wFechaFrom = new sfWidgetFormInputText(array('label' => ' '),array('class'=>'dateFrom','size'=>8));        
                                $vFechaFrom = new sfValidatorDateTime(array('required' => false));
                                $vFechaFrom->setOptions(array('required'=>false, 'date_format'=>'#(?P<day>\d{2})(?P<month>\d{2})(?P<year>\d{2})#',
                                                                                           'date_output' => '20y-m-d 00:00:00' ));
                                $vFechaFrom->setMessages(array('invalid' => 'Fecha inválida.'));            


                                $wFechaTo = new sfWidgetFormInputText(array('label' => ' '),array('class'=>'dateTo','size'=>8));        
                                $vFechaTo = new sfValidatorDateTime(array('required' => false));
                                $vFechaTo->setOptions(array('required'=>false, 'date_format'=>'#(?P<day>\d{2})(?P<month>\d{2})(?P<year>\d{2})#',
                                                                                           'date_output' => '20y-m-d 23:59:59' ));
                                $vFechaTo->setMessages(array('invalid' => 'Fecha inválida.'));            

                                $widget = new sfWidgetFormFilterDate(array('from_date' => $wFechaFrom, 'to_date' => $wFechaTo));
                                $widget->setOption('template', 'desde: %from_date% hasta: %to_date%');            
                                $widget->setOption('with_empty', false);   
                                
                                
                                if(($label = $th->getFilterLabel()) != '')
                                {
                                    $widget->setLabel($label.' (ddmmyy):');      
                                }else
                                {
                                    $widget->setLabel($this->thSchema[$k]->getText().' (ddmmyy):');                                          
                                }

                                $this->filter->setWidget($k, $widget);                                
                                
                                $validator = new sfValidatorDateRange(array('required' => false, 'from_date' => $vFechaFrom, 'to_date' => $vFechaTo));
                                $validator->setMessage('invalid', 'La fecha de inicio debe ser posterior que la de final.');
                                $this->filter->setValidator($k, $validator);


                            break;
                            
                            case 'enum':
                                $items = $th->getFilterItems();
                                $widget = new sfWidgetFormChoice(array('choices' => $items));                                
                                if(($label = $th->getFilterLabel()) != '')
                                {
                                    $widget->setLabel($label);      
                                }else
                                {
                                    $widget->setLabel($this->thSchema[$k]->getText());                                          
                                }
                                
                                $this->filter->setWidget($k, $widget);

                                $validator = new sfValidatorChoice(array('required' => false, 'choices' => array_keys($items)));
                                $this->filter->setValidator($k, $validator);

                            break;

                            case 'mark':  //Este tipo es igual al anterior salvo porque carga el mismo los items
                                $items = array(1=>'Si',
                                               0=>'No',
                                              ''=>'Indistinto');
                                $th->setFilterItems($items);
                                $th->setFilterType('enum');
                                $this->filter->filterTypes[$k] = 'enum'; //tengo que cambiar el tipo de filtro 
                                
                                $widget = new sfWidgetFormChoice(array('choices' => $items));                                
                                if(($label = $th->getFilterLabel()) != '')
                                {
                                    $widget->setLabel($label);      
                                }else
                                {
                                    $widget->setLabel($this->thSchema[$k]->getText());                                          
                                }
                                
                                $this->filter->setWidget($k, $widget);

                                $validator = new sfValidatorChoice(array('required' => false, 'choices' => array_keys($items)));
                                $this->filter->setValidator($k, $validator);

                            break;

                            case 'multi':
                                $items = $th->getFilterItems();
                                $widget = new sfWidgetFormSelectCheckbox(array('choices' => $items));                                
                                if(($label = $th->getFilterLabel()) != '')
                                {
                                    $widget->setLabel($label);      
                                }else
                                {
                                    $widget->setLabel($this->thSchema[$k]->getText());                                          
                                }
                                
                                $this->filter->setWidget($k, $widget);

                                $validator = new sfValidatorChoice(array('required' => false, 'multiple' =>true, 'choices' => array_keys($items)));
                                $this->filter->setValidator($k, $validator);
                                //$th->setFilterType('');
                            break;
                        }
                } else
                {
                    $this->filter->setWidget($k, $th->getWidget());
                    $this->filter->setValidator($k, $th->getValidator());
                }
                
                
                //if customFilter is set use this
                if(($customFilter = $th->getOption('customFilter')) != '')
                        $this->filter->filterTypes[$k] = $customFilter;
                else
                {
                }
                
            
            }

        }
        
        ksort($positionsArray);
        $this->filter->useFields(array_values($positionsArray));
    }
    
    public function bind($filterValues, $tableValues) {
        if($filterValues){
            $this->filter->bind($filterValues);        
        }
        
    }

    
    public function generateAlias() {

        $aliasArray[$this->getOption('model')] =array('alias'=>'a0');
        $aliasNum = 1;

        foreach($this->tdSchema as $k => $td)
        {
            if($td->isModelTd())
            {
 

                
                $route =$td->getField();

                $nodes = explode('.',$route);

                $td->setOption('leaf', $nodes[count($nodes)-1]);
                
                unset($nodes[count($nodes)-1]);
                

                    
                if(count($nodes)>0)
                {
                    $cmd = '$aliasArray[\''.$this->getOption('model').'\']';
                    foreach($nodes as $node)
                    {
                        $cmd .='[\''.$node.'\']';
                        eval($cmd.'[\'alias\'] = "a'.$aliasNum++.'";');                        
                    }

                    //$cmd.='[\'alias\'] = "a'.$aliasNum++.'";';


                }
 
            }
        }
        
        $this->aliasArray = $aliasArray;
//        print_r($this->aliasArray);        
        //Armo los joins en base a los Td
//               $res= $modelArray[$this->getOption('model')]['Afiliado']['numeroAfiliado']['alias'];
//               
//               echo $res;
        
    }

    public function getAlias($route) {

        $aliasArray = $this->aliasArray;

        $nodes = explode('.',$route);
        
        unset($nodes[count($nodes)-1]);

        $cmd = '$result = $aliasArray[\''.$this->getOption('model').'\']';        
        
        foreach($nodes as $k => $node)
        {
            $cmd .='[\''.$node.'\']';
        }

        $cmd.='[\'alias\'];';


        eval($cmd);
                
        return $result;

                
    }

    //Funcion recursiva para armar los left joins en funcion del arbol de alias
    public function buildJoins($aliasArray,$aliasPath = '') {
        if(isset($aliasArray['alias']))
        {
            $alias = $aliasArray['alias'];
            $aliasPath = $alias.'.';
            unset($aliasArray['alias']);            
        }

        foreach($aliasArray as $k=>$subArray)
        {
            if(isset($subArray['alias']))
            {
                $this->query->leftJoin($aliasPath.$k.' '.$subArray['alias']);
                $this->query->addSelect($subArray['alias'].'.id');
                $this->buildJoins($subArray, $aliasPath);
            }else
            {
                $this->buildJoins($subArray, $aliasPath.$k.'.');
                
            }
            //if($alias)
            
        }
        return;
    }   
        
    
    public function buildQuery($request) {    
        
        $filterValues = $request->getParameter($this->getFilterParameters());        
        $tableValues = $request->getParameter($this->getTableParameters());        

        $this->generateAlias();

        $this->buildJoins($this->aliasArray[$this->getOption('model')]);
            
        
        
        foreach($this->tdSchema as $k => $td)
        {
            if($td->isModelTd())
            {
                //if(!$td->isHidden())
                {

                
                    $alias = $this->getAlias($this->tdSchema[$k]->getField());
                    $leaf = $this->tdSchema[$k]->getOption('leaf');
                    $this->query->addSelect($alias.'.'.$leaf);
                    //$this->query->addSelect($alias.'.id');                    
                }
            }
        }

                    //$this->query->addSelect('a8.id');        
        
        foreach($this->thSchema as $k => $th)
        {
            $fixedConditions = $th->getFixedConditions();
            //me fijo si hay fixed conditions
            if($fixedConditions != array())
            {
                $alias = $this->getAlias($this->tdSchema[$k]->getField());
                $leaf = $this->tdSchema[$k]->getOption('leaf');
                    
                foreach($fixedConditions as $fixedCondition)
                {
                    //call_user_func_array('sprintf', $values);
                    $this->query->andWhere($alias.'.'.$leaf.' '.$fixedCondition);

                }

            }

        }
        

        
        
        $this->configureFilter();

        $this->bind($filterValues, null);
        
        $this->filter->setQuery($this->query);

        if($this->filter->isValid()){
            $this->query = $this->filter->getQuery();
        }    
        
        
        //guardo la query sin ordenar ni paginar pero si filtrada.
        
        if($this->orderBy != null)
        {
            $this->orderBy->setQuery($this->query);
            $this->orderBy->bind($tableValues);
            $this->query = $this->orderBy->execute();
        }
    
        $this->baseQuery = $this->query->copy();        
        
        return $this->query;
    }
    
    
    public function execute($request) {

        $tableValues = $request->getParameter($this->getTableParameters());        
        
        if($this->pager != null)
        {
            $this->pager->setQuery($this->query);
            $this->pager->bind($tableValues);
            $this->query = $this->pager->execute();
        }
        
        
        
        $this->data = $this->query->execute(array(),Doctrine_Core::HYDRATE_ARRAY);
        
    }
    
    
    public function __toString() {
        return $this->render();
    }
    
    public function preRender() {
    
        return form_tag(url_for($this->getOption('action'),array('method'=>'get')));       
    }

    public function renderFilter() {

        //color for switch
        
        $color1 = '#7F0000';
        $color2 = '#00007F';                

        
        foreach($this->filter as $field=>$row)
        {
            $th = $this->thSchema[$field];
            if($th->isFilterable())$struct[$th->getOption('filterGroup')][$field] =  $th->getFilter();
        }
        
        $result = '';
        $resultArray = array();
        
        $valores = $this->filter->getTaintedValues();        
                
        foreach($struct as $groupName => $group)
        {
            $filterG = '';
            $accordionClass = $this->getName().'Accordion';
            $usedGroup = false;
            $groupError = false;
            $resumen = '  ';
            $fieldColor=$color1;
            
            foreach($group as $field =>$used)
            {
                $usedGroup = $usedGroup || $used;
                $filterG .= $this->filter[$field]->renderRow();
                $filterField = $this->filter[$field];
                if($filterField->hasError()) {$groupError = true;}
                if($used)
                { 
                    if($fieldColor==$color1)
                    {
                        $fieldColor=$color2;
                    }else
                    {
                        $fieldColor=$color1;
                    }
                    
                    $resumen .= '<span style="color:'.$fieldColor.';">';
                    
                    $th = $this->thSchema[$field];
                    $type = $th->getFilterType();
                    switch ($type)
                    {
                        case 'multi':
                            $resumen .= ' ';
                            $items = $th->getFilterItems();
                            $valorCompuesto = $valores[$field];
                            foreach($valorCompuesto as $valor)
                                $resumen.=$items[$valor].'-';
                        break;
                        
                        case 'enum':
                            $resumen .= ' '.$this->filter->getWidget($field)->getOption('label').': ';
                            $items = $th->getFilterItems();
                            $resumen.=' '.$items[$valores[$field]];
                        break;

                        case 'dateRange':
                            $resumen .= ' '.$this->filter->getWidget($field)->getOption('label').' ';
                            
                            $desdeBarras = substr($valores[$field]['from'], 0, 2) .'/'. 
                                    substr($valores[$field]['from'], 2, 2).'/'.
                                    substr($valores[$field]['from'], 4, 2);
                            
                            $hastaBarras = substr($valores[$field]['to'], 0, 2) .'/'. 
                                    substr($valores[$field]['to'], 2, 2).'/'.
                                    substr($valores[$field]['to'], 4, 2);
                            
                            $resumen.=$desdeBarras.' hasta '.$hastaBarras;
                        break;
                    
                        default:
                            $resumen .= ' ';
                            $valorCompuesto = $valores[$field];
                            if(is_array($valorCompuesto))
                            {
                                foreach($valorCompuesto as $valor)
                                    $resumen.=$valor;
                            }else
                            {
                                $resumen.=$valorCompuesto;
                            } 
                        break; 
                    }
                    

                    $resumen .= '</span>';
                }
            }
            
            
            $accordionClass .= ($groupError) ? 'Error': ( ($usedGroup) ? 'Used' : '');
            
            
            //.'<input type="button" style="font-size:60%; margin-left: 500px;" '.'value="1 MES"'.'>                    
            $resultArray['%'.$groupName.'%'] = $this->renderContentTag('div',
                        $this->renderContentTag('h3',$groupName.'<span style="color:red; font-size:60%;">'.$resumen.' </span> ').
                        $this->renderContentTag('div',
                            $this->renderContentTag('table', $filterG.
                                        $this->renderContentTag('tr',
                                            $this->renderContentTag('td',
                                                $this->renderTag('input',
                                                            array('type'=>"submit", 'value'=>"Filtrar",'style'=>'float:right','class'=>'tableButton')),array('colspan'=>'100'))))),
                        array('style'=>'float:top','class'=>$accordionClass));
            
        }
        
        if($resultArray != array())
        {
            $result = strtr($this->getOption('filterTemplate'),$resultArray);
        }
        
        return $result;
    }
    
    public function renderTable() {
        
        $result = ''; 
        
        $tdSchemaList = '';
        
        foreach($this->data as $row)
        {
            $tdSchemaList .= $this->tdSchema->render($row).chr(10);            
        }
        
        $footer = ($this->getOption('footerContent') != '') ? 
                   $this->renderContentTag('tfoot',$this->getOption('footerContent')) : '';
        
        $result .= $this->renderContentTag('table',
                        $this->renderContentTag('thead',$this->thSchema->render()).
                        $this->renderContentTag('tbody',$tdSchemaList).
                        $footer,$this->getAttributes());            
        
        
        $fixedParametersInput = '';
        
        
        foreach($this->getOption('fixedParameters') as $k=>$fixedParameter)
        {
            $fixedParametersInput .= $this->renderTag('input', array('name'=>$k, 
                                                            'value'=>$fixedParameter,
                                                            'type'=>'hidden'));
        }
        
        $result .= $this->renderContentTag('table',
                        $this->renderContentTag('tr',
                            $this->renderContentTag('td',$fixedParametersInput)
                        ),array('display'=>'none'));            
        
        
        return $result;
        
    }

    
    public function renderPager() {
        
        $result = ''; 
        

        if($this->pager != null)
        {
            $result .= $this->renderContentTag('table', $this->pager->render(),array('class'=>$this->getName()."Pager",'style'=>"float:right;"));                           
        }
        
        return $result;
        
    }

    public function renderOrderBy() {
        
        $result = ''; 
        
        if($this->orderBy != null)
        {
            $result .= $this->renderContentTag('table', $this->orderBy->render(),array('style'=>'float:right; margin-top:5px; margin-bottom:5px; border: 1px solid #CCCCCC;'));                                       
        }
        
        return $result;
        
    }
    
    public function postRender() {
        
        $result= '';
        
        $result .= '</form>'.chr(10);                       
        
        return $result;        
    }
    
    
    public function render($content = '') {
        
        $result= '';

        $result .= $this->renderCSS();
        
        $result .= $this->renderJS();        
        
        $result .= $this->renderContentTag('form', strtr($this->getTemplate(),array(
                                                                        '%filter%'=>$this->renderFilter(),
                                                                        '%pager%'=>$this->renderPager(),
                                                                        '%orderBy%'=>$this->renderOrderBy(),
                                                                        '%table%'=>$this->renderTable())
                                                ),array('method'=>'get','action'=>url_for($this->getOption('action'))));
        return $result;
    }

    
    
    public function renderCSS() {

        $result= '';
        $CSS = '
       
    .conFiltros
    {
        background-image: url("'.image_path('/images/icons/accConFiltros.png',true).'") !important;               
        width: 25px !important;
        height: 25px !important;
    }
    .conError
    {
        background-image: url("'.image_path('/images/icons/accError.png',true).'") !important;               
        width: 25px !important;
        height: 25px !important;
    }
';
        
        
        $result = $this->renderContentTag('style',$CSS,array('type'=>'text/css'));
        
        return $result;
    }

    public function renderJS() {

        $result= '';
        $JS = '
  $(function() {
      
    $("#nuevaOrdenBtn").button();

    $(".tableButton").button();
      
      $( ".dateFrom" ).datepicker({
             showOn: "button",           
             dateFormat: "ddmmy",
             defaultDate: "",
             changeMonth: true,
             onClose: function( selectedDate ) {
                 $( "#hasta" ).datepicker( "option", "minDate", selectedDate )
                 if($( "#hasta" ).attr("value")=="")$( "#hasta" ).datepicker( "show" );
             }
     });
     
     $( ".dateTo" ).datepicker({
             showOn: "button",
             dateFormat: "ddmmy",
             defaultDate: "",
             changeMonth: true,
             onClose: function( selectedDate ) {
                 $( "#desde" ).datepicker( "option", "maxDate", selectedDate );
             }
     });
  });
    

  $(document).ready(function() {

    $( ".'.$this->getName().'AccordionUsed" ).accordion({
      collapsible: true, active: 1
    });
    $(".'.$this->getName().'AccordionUsed").accordion("option", "icons",
        { "header": "conFiltros", "headerSelected": false });
        

    $( ".'.$this->getName().'AccordionError" ).accordion({
      collapsible: true, active: 1
    });
    
    $(".'.$this->getName().'AccordionError").accordion("option", "icons",
        { "header": "conError", "headerSelected": false });
        

    $( ".'.$this->getName().'Accordion" ).accordion({
      collapsible: true, active: 1
    });
    
  
    
  });  
  



';
        
        
        $result = $this->renderContentTag('script',$JS,array());
        
        return $result;
    }
    
}


?>
